package com.csap.framework.oauth2.client.user.strategy;

import cn.dev33.satoken.LoginUtil;
import cn.dev33.satoken.config.SaTokenConfig;
import com.csap.framework.core.ApplicationContextHolder;
import com.csap.framework.oauth2.FindUserService;
import com.csap.framework.oauth2.IUserDataCache;
import com.csap.framework.oauth2.UserInfo;
import com.csap.framework.oauth2.UserInfoMod;
import com.csap.framework.oauth2.UserInfoStoreService;
import com.csap.framework.oauth2.client.Oauth2ClientProperties;
import com.csap.framework.oauth2.client.rest.Oauth2ClientApi;
import com.csap.framework.redis.RedisUtil;
import com.csap.framework.util.Assert;
import com.csap.framework.util.CollectionsUtil;
import com.csap.framework.util.optional.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;

import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * redis缓存查询
 *
 * @author yangchengfu
 * @dataTime 2021年-05月-16日 16:04:00
 **/
@Slf4j
public class RedisFindUserServiceImpl implements FindUserService, UserInfoStoreService, IUserDataCache {
    @Getter
    private final RedisUtil redisUtil;
    private final SaTokenConfig saTokenConfig;
    private final Oauth2ClientProperties oauth2ClientProperties;
    private FindUserService findUserService;

    public RedisFindUserServiceImpl(RedisUtil redisUtil, ApplicationContext applicationContext, SaTokenConfig saTokenConfig, Oauth2ClientProperties oauth2ClientProperties) {
        this.redisUtil = redisUtil;
        this.saTokenConfig = saTokenConfig;
        this.oauth2ClientProperties = oauth2ClientProperties;
        Collection<Oauth2ClientApi> values = applicationContext.getBeansOfType(Oauth2ClientApi.class).values();
        if (CollectionsUtil.isNotEmpty(values)) {
            this.findUserService = new FeignFindUserServiceImpl(values.iterator().next(), oauth2ClientProperties);
        } else {
            Assert.notEmpty(oauth2ClientProperties.getOauth2ServerUrl(), "授权服务器的URL地址不能为空");
            this.findUserService = new HttpFindUserServiceImpl(oauth2ClientProperties, saTokenConfig);
        }
    }

    @Override
    public UserInfoMod mod() {
        return UserInfoMod.REDIS;
    }

    @Override
    public UserInfo findByUserId(Object loginId) {
        return findByUserId(loginId, LoginUtil.loginType());
    }

    @Override
    public UserInfo findByUserId() {
        return findByUserId(LoginUtil.getInstance().getLoginId());
    }

    @Override
    public UserInfo findByUserId(Object loginId, String loginType) {
        AtomicReference<Boolean> ret = new AtomicReference<>(Boolean.FALSE);
        return Optional.ofNullable((UserInfo) getStore(LoginUtil.getInstance().cachekey(loginId), USERINFO, () -> accept(ret), oauth2ClientProperties.getCacheTime()))
                .then(i -> i.setRoleIds(getStore(LoginUtil.getInstance().cachekey(loginId), ROLE, () -> accept(ret), oauth2ClientProperties.getCacheTime())))
                .then(i -> i.setUserAttribute(getStore(LoginUtil.getInstance().cachekey(loginId), USER_ATTRIBUTE, () -> accept(ret), oauth2ClientProperties.getCacheTime())))
                .then(i -> rolePermissions(i, ret))
                .value(i -> {
                    if (Objects.nonNull(i) && i.getRoleIds().size() != i.getRoleList().size()) {
                        ret.set(true);
                    }
                    if (Objects.isNull(i) || ret.get()) {
                        log.warn("findByUserId cache result is null,send http request:{},{}", loginId, loginType);
                        return findUserService.findByUserId(loginId, loginType);
                    } else {
                        return i;
                    }
                });
    }

    /**
     * 角色的权限
     */
    private void rolePermissions(UserInfo userInfo, AtomicReference<Boolean> ret) {
        Set<String> strings = userInfo.getRoleIds().stream().map(String::valueOf).collect(Collectors.toSet());
        userInfo.setRoleList(getRoleList(strings, i -> accept(ret)));
        Set<String> permissions = getRolePermissions(strings, i -> accept(ret));
        if (Objects.isNull(permissions)) {
            permissions = Sets.newHashSet();
        }
        userInfo.setPermissionsList(Lists.newArrayList(permissions));
    }

    /**
     * 统一的数据处理
     *
     * @param ret 操作处理结果
     * @param <T> 结果
     * @return 结果
     */
    private <T> T accept(AtomicReference<Boolean> ret) {
        ret.set(true);
        return null;
    }
}
